home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000 #2
/
Ham Radio 2000 - Volume 2.iso
/
HAMV2
/
MISC
/
DTMFF110
/
FREQ.C
< prev
next >
Wrap
C/C++ Source or Header
|
1997-08-05
|
14KB
|
525 lines
/*
* Program: FREQ.C
* Author: Philip VanBaren & Emil LAURENTIU
* Date: 15 August 1993
* Last modified: Tuesday, 05 August 1997
*
* Description: This program samples data from a sound card, performs an FFT,
* and displays the result.
* Can handle up to 2048 points (actually any size is possible
* with a little fiddling of buffers to get around 64k limits).
* (This restriction is given freq.h, and may be changed.)
* On a 486/33 this code can perform and plot 1024-point and
* below in nearly real-time at 44100kHz. (1024 FFT=31ms)
*
* The DOS portion of this code was written for Borland C, but should work
* with other compilers if the graphics and console-io base functions are
* changed appropriately.
*
* The source for specific graphics environments and sound cards may require
* other packages. Refer to the specific files for more details.
*
* Most changes are required only in the sc_*.c and gr_*.c files.
*
* Copyright (C) 1995 Philip VanBaren (C) Emil LAURENTIU
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "freq.h"
#include "fft.h"
#include "extern.h"
#include "display.h"
/*
* Table for approximating the logarithm.
* These values are round(log2(index/16)*8192) for index=0:31
*/
long ln[] = {
-131072L, -32768L, -24576L, -19784L, -16384L, -13747L,
-11592L, -9770L, -8192L, -6800L, -5555L, -4428L, -3400L,
-2454L, -1578L, -763L, 0L, 716L, 1392L, 2031L, 2637L, 3214L,
3764L, 4289L, 4792L, 5274L, 5738L, 6184L, 6614L, 7029L, 7429L,
7817L };
int f_dtmf[8] = {697, 770, 852, 941, 1209, 1336, 1477, 1633};
int o_dtmf[8] = {18, 20, 22, 24, 31, 34, 37, 42};
int sb_f_dtmf[8] = {457, 505, 558, 617, 396, 438, 484, 535};
char matrix_dtmf[4][4] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'} };
int on_dtmf[8];
char dtmf_nr[121];
int p_dtmf;
int last_i = -1;
int active_freq, active_dtmf = 0;
int active_ctcss = 0;
int ctcss_nr;
double f_ctcss[] = {
67.0, 69.4 , 71.9, 74.4, 77.0, 79.7, 82.5, 85.4, 88.5, 91.5,
94.8, 97.4, 100.0, 103.5, 107.2, 110.9, 114.8, 118.8, 123.0,
127.3, 131.8, 136.5, 141.3, 146.2, 151.4, 156.7, 159.8, 162.2,
165.5, 167.9, 171.3, 173.8, 177.3, 179.9, 183.5, 186.2, 189.9,
192.8, 196.6, 199.5, 203.5, 206.5, 210.7, 218.1, 225.7, 229.1,
233.6, 241.8, 250.3, 254.1 };
unsigned long ctcss_act1 = 0xABFFFFFD;
unsigned long ctcss_act2 = 0x0001DD2A;
long m_ctcss;
char sline[80];
volatile int flag[BUFFERS]; /* Array of flags indicating fullness of
* buffers */
volatile int record_buffer; /* Pointer to next buffer to be filled */
int queue_buffer; /* Pointer to next buffer to be queued */
int process_buffer; /* Pointer to next buffer to be FFTed */
short *fftdata; /* Array for FFT data */
short *wind; /* Array storing windowing function */
int x[WINDOW_RIGHT - WINDOW_LEFT + 1]; /* Array of bin #'s
* displayed */
int x2[WINDOW_RIGHT - WINDOW_LEFT + 1]; /* Array of terminal bin
* #'s */
int lasty[WINDOW_RIGHT - WINDOW_LEFT + 1]; /* Last y position for
* FFT bins */
unsigned int yscale[WINDOW_RIGHT - WINDOW_LEFT + 1]; /* Scaling factors */
long *ybase; /* Scaling offset for log calculations */
long *displayval;
int shift = 0; /* Number of bits for gain shift */
double shiftscale = 1; /* Multiplication factor that does this shift */
float log_scalefactor; /* Scaling factor for log values */
float disp_scalefactor; /* Display scalefactor for log values */
void far *buffer[BUFFERS]; /* Buffers for gathering data */
short *p1, *p2; /* Various indexing pointers */
int *bri;
long *pDisplayval;
int *pLasty;
long *pYbase;
unsigned int *pYscale;
int *pX, *pX2;
unsigned char far *sample8;
short far *sample16;
long a2, root, mask; /* Variables for computing Sqrt/Log of
* Amplitude^2 */
long peak_amp; /* Peak amplitude found */
int peak_index; /* Bin number of the peak amplitude */
long back1, back2; /* Variables for differencing */
char ini_file[100]; /* Filename for the ini file */
int done = 0; /* Flag indicating program should exit */
int
main( int argc, char *argv[], char *environ[] )
{
int i, j, ind;
int padX, padY;
long y;
int first;
int key = 0;
draw_init( );
DOUT( "Getting the command line arguments" );
/*
* Check if the first parameter is an ini file name
*/
if ( argc > 1 && argv[1][0] != '-' && argv[1][0] != '/' )
{
strncpy( ini_file, argv[1], sizeof( ini_file ) );
first = 2;
}
else
{
strncpy( ini_file, "dtmf_fft.ini", sizeof( ini_file ) );
first = 1;
}
/*
* Parse the ini file and command line
*/
DOUT( "Parsing the ini file" );
parse_ini_file( );
DOUT( "Parsing the command line" );
parse_command( ( argc - first ), &argv[first], environ );
/*
* Initialize the buffer info for the maximum size we will encounter
*/
DOUT( "Allocating the buffer space" );
setup_buffers( MAX_LEN );
DOUT( "Computing the window functions" );
compute_window_function( );
/*
* Set up the required arrays in the FFT code.
*/
DOUT( "Initializing the FFT code" );
InitializeFFT( fftlen );
/*
* Initialize the graphics to 640x480 VGA mode
*/
DOUT( "Setting the graphics mode" );
setup_graphics( );
setnormalpalette( );
draw_fontcolor( TEXT_COLOR );
draw_rectangle( WINDOW_LEFT - 2, WINDOW_TOP - 2,
WINDOW_RIGHT + 2, WINDOW_BOTTOM + 2, BORDER_COLOR );
DOUT( "Resetting the sound card" );
reset_soundcard( );
/*
* Initalize the graph scales
*/
setup_xscale( );
amplitude_scale( );
DOUT( "Drawing the header information" );
update_header( );
/*
* Keep getting data and plotting it. A space will pause, a second space
* will continue. Any other key will quit.
*/
DOUT( "Entering data loop" );
while ( !done )
{
/* Wait for current buffer to fill up, and check for an input */
int input;
key = draw_getkey( );
#ifdef DEBUG_MODE
i = 0;
while ( i++ < 500 && !key )
#else
while ( ( !flag[process_buffer] || freeze ) && !key )
#endif
key = draw_getkey( );
input = key;
while ( input )
{
/* Grab and count repeated keystrokes */
int repetitions = 1;
key = input;
input = draw_getkey( );
while ( input == key )
{
repetitions++;
input = draw_getkey( );
}
done |= process_input( key, repetitions );
}
if ( !key && !freeze )
{
int clip = 0;
/*
* Perform windowing on the data
*/
p1 = fftdata;
p2 = wind;
if ( sample_size == 8 )
{
sample8 = ( unsigned char far * ) buffer[process_buffer];
for ( i = 0; i < fftlen; i++ )
{
#ifdef DEBUG_MODE
*sample8 = ( char ) rand( );
#endif
if ( ( *sample8 == 0 ) || ( *sample8 == 255 ) )
clip = 1;
*p1 = ( short ) ( ( ( ( long ) ( *sample8 ) - 128L ) * ( long ) ( *p2 ) ) >> 7 );
sample8++;
p1++;
p2++;
}
}
else
{
sample16 = ( short far * ) buffer[process_buffer];
for ( i = 0; i < fftlen; i++ )
{
#ifdef DEBUG_MODE
*sample16 = rand( );
#endif
if ( ( *sample16 == 32767 ) || ( *sample16 == -32768L ) )
clip = 1;
*p1 = ( short ) ( ( ( long ) ( *sample16 ) * ( long ) ( *p2 ) ) >> 15 );
sample16++;
p1++;
p2++;
}
}
if ( clip )
draw_setpalette( 0, warn.red, warn.green, warn.blue );
else
draw_setpalette( 0, background.red, background.green, background.blue );
/* Free up the buffer we just processed. */
flag[process_buffer] = 0;
if ( ++process_buffer >= BUFFERS )
process_buffer = 0;
/* Now that we have processed the buffer, queue it up again. */
recordblock( buffer[queue_buffer] );
if ( ++queue_buffer >= BUFFERS )
queue_buffer = 0;
/* The real meat of the code lies elsewhere! */
RealFFT( fftdata );
/* Use pointers for indexing to speed things up a bit. */
bri = BitReversed;
pDisplayval = displayval;
for ( i = 0; i < fftlen / 2; i++ )
{
/* Compute the magnitude */
register long re = fftdata[*bri];
register long im = fftdata[( *bri ) + 1];
register long root;
if ( ( a2 = re * re + im * im ) < 0 )
a2 = 0; /* Watch for possible overflow */
/* Use higher resolution only for small values */
if ( a2 > 4194304L )
{
root = 32;
do
{
mask = a2 / root;
root = ( root + mask ) >> 1;
} while ( labs( root - mask ) > 1 );
root *= 16;
}
else
{
root = 512;
a2 *= 256;
do
{
mask = a2 / root;
root = ( root + mask ) >> 1;
} while ( labs( root - mask ) > 1 );
}
*pDisplayval = root;
bri++;
pDisplayval++;
}
}
if ( dtmf_mode )
{
active_freq = 0;
for ( i = 0; i < 8; i++ )
{
/* ind = (int)( (double)(f_dtmf[i])*fftlen/SampleRate +.5 ); */
if ( displayval[o_dtmf[i]] > 524288.0 * threshold_level )
{
on_dtmf[i] = 1;
active_freq += ( i < 4 ) ? 1 : 10;
}
else
on_dtmf[i] = 0;
}
if ( active_freq == 11 ) /* DTMF means 2 freq's active */
{
padX = 1 * on_dtmf[4] + 2 * on_dtmf[5] + 3 * on_dtmf[6] + 4 * on_dtmf[7] - 1;
padY = 1 * on_dtmf[0] + 2 * on_dtmf[1] + 3 * on_dtmf[2] + 4 * on_dtmf[3] - 1;
i = padX + 4 * padY;
if( log_mode && !active_dtmf )
fprintf( log_file, "%s - '", time_stamp() );
if ( i != last_i || !active_dtmf )
{
dtmf_nr[p_dtmf] = matrix_dtmf[padY][padX];
dtmf_nr[p_dtmf + 1] = 0;
last_i = i;
draw_fontcolor( LABEL_COLOR );
draw_text_left( 156 + 8 * ( p_dtmf % 40 ), 60 + 10 * ( p_dtmf / 40 ),
&dtmf_nr[p_dtmf] );
if( log_mode )
fprintf( log_file, "%c", dtmf_nr[p_dtmf] );
p_dtmf++;
if ( p_dtmf == 120 )
{
p_dtmf = 0;
draw_bar( 156, 60, 476, 90, 0 );
}
}
active_dtmf = 1;
}
else
{
if( log_mode && active_dtmf )
fprintf( log_file, "'\n" );
active_dtmf = 0;
}
}
if ( ctcss_mode )
{
draw_bar( 200, 60, 240, 70, 0 );
draw_fontcolor( LABEL_COLOR );
draw_text_left( 200, 60, "off" );
m_ctcss = 0L;
for ( i = 0; i < CTCSS_MAX; i++ )
{
if( ( i < 32 ? ctcss_act1 >> i : ctcss_act2 >> (i-32) ) & 1 )
{
ind = ( int ) ( f_ctcss[i] * fftlen / SampleRate + .5 );
if ( displayval[ind] > m_ctcss )
{
m_ctcss = displayval[ind];
ctcss_nr = i;
}
}
}
if ( m_ctcss > 524288.0 * threshold_level )
{
draw_bar( 124, 60, 240, 70, 0 );
draw_fontcolor( GRAPH_COLOR );
sprintf( sline, "%5.1lf Hz", f_ctcss[ctcss_nr] );
draw_text_left( 124, 60, sline );
draw_fontcolor( LABEL_COLOR );
draw_text_left( 200, 60, "on" );
if( log_mode && (last_i != ctcss_nr || !active_ctcss ) )
{
fprintf( log_file, "%s - %5.1lf Hz\n", time_stamp(),
f_ctcss[ctcss_nr] );
last_i = ctcss_nr;
}
active_ctcss = 1;
}
else
active_ctcss = 0;
}
{
/*
* Next, put this data up on the display
*/
setup_vga( ); /* Prepare VGA for video update */
pLasty = lasty;
pX = x;
pX2 = x2;
pYscale = yscale;
peak_amp = 0;
peak_index = 0;
y = WINDOW_BOTTOM;
/* For linear amplitude mode */
{
int index, xval;
for ( i = WINDOW_LEFT; i < WINDOW_RIGHT + 1; i++ )
{
/*
* If this line is the same as the previous one, just use the
* previous y value. Else go ahead and compute the value.
*/
index = *pX;
if ( index != -1 )
{
register long dv = displayval[index];
if ( *pX2 ) /* Take the maximum of a set of bins */
{
for ( xval = index; xval < *pX2; xval++ )
{
if ( displayval[xval] > dv )
{
dv = displayval[xval];
index = xval;
}
}
}
y = ( WINDOW_BOTTOM ) - ( ( dv * *pYscale ) >> shift );
if ( y < WINDOW_TOP )
y = WINDOW_TOP;
if ( dv > peak_amp )
{
peak_amp = dv;
peak_index = *pX;
}
}
if ( y > *pLasty )
{
/* Draw a black line */
unsigned char bit = ~( 0x80 >> ( i & 0x07 ) );
unsigned int endbase = ( unsigned int ) ( y * 80 );
unsigned int base = ( unsigned int ) ( *pLasty * 80 + ( i >> 3 ) );
while ( base < endbase )
{
screen( base ) &= bit;
base += 80;
}
}
else
{
/* Draw a blue line. */
unsigned char bit = 0x80 >> ( i & 0x07 );
unsigned int endbase = ( unsigned int ) ( ( *pLasty + 1 ) * 80 );
unsigned int base = ( unsigned int ) ( y * 80 + ( i >> 3 ) );
while ( base < endbase )
{
screen( base ) |= bit;
base += 80;
}
}
*pLasty = ( unsigned int ) y;
pDisplayval++;
pX++;
pX2++;
pLasty++;
pYscale++;
}
}
cleanup_vga( ); /* Reset VGA for normal functions */
}
if ( display_peak )
{
char ach[20];
sprintf( ach, "%7.1f", ( double ) SampleRate * peak_index / fftlen );
draw_bar( PKX, PKY - 1, PKX + 63, PKY + _font_height, 0 );
draw_text_left( PKX, PKY, ach );
}
}
/*
* Shut down the DMA system.
*/
cleanup_soundcard( );
cleanup_graphics( );
if( log_mode )
fclose( log_file );
printf( "You have been using DTMF_FFT v 1.10 (dtmf & ctcss) " );
#ifdef SC_SB8
if ( Soundcard == SC_SB8 )
printf( " in Soundblaster 8-bit mode." );
#endif
#ifdef SC_SB16
if ( Soundcard == SC_SB16 )
printf( " in Soundblaster 16-bit mode." );
#endif
printf( "\nCopyright (C) 1996 Philip VanBaren & "
"(C) 1997 Emil Laurentiu (YO3GGH)" );
return ( 0 );
}